
package com.jf.cloud.seckill.task;

import cn.hutool.core.collection.CollectionUtil;
import com.jf.cloud.common.cache.constant.CacheNames;
import com.jf.cloud.common.cache.constant.LockCacheNames;
import com.jf.cloud.common.cache.constant.SeckillCacheNames;
import com.jf.cloud.common.cache.util.RedisUtil;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.seckill.constant.SeckillConstant;
import com.jf.cloud.seckill.model.Seckill;
import com.jf.cloud.seckill.service.SeckillService;
import com.jf.cloud.seckill.service.SeckillSkuService;
import com.jf.cloud.seckill.vo.SeckillSkuVO;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;


/**
 * 秒杀订单的定时任务
 * @author zz
 */
@Component
public class SeckillTask {

    @Autowired
    private SeckillService seckillService;

    @Autowired
    private SeckillSkuService seckillSkuService;

    @Autowired
    private RedissonClient redissonClient;


    /**
     * 秒杀结束，改变商品类型
     */
    @XxlJob("activityFinishAndProdChange")
    public void activityFinishAndProdChange(){
        XxlJobHelper.log("秒杀活动结束，秒杀商品恢复为普通商品。。。");
        // 获取活动结束，需要改变商品类型的列表
        List<Seckill> seckillList = seckillService.listUnEndButNeedEndActivity();
        if (CollectionUtil.isEmpty(seckillList)) {
            return;
        }
        seckillService.changeProdTypeBySeckillIdList(seckillList);
    }


    /**
     * 正在进行的秒杀活动库存落库
     * 秒杀库存落库，每10秒进行一遍
     * 什么秒杀活动需要落库呢？因为我们目前的秒杀活动都是通过缓存扣除库存的
     * @see com.jf.cloud.seckill.service.impl.SeckillOrderServiceImpl#reduceStockByZone(Long,Integer ,int ,Long)
     */
    @XxlJob("hotStockPlacement")
    public void hotStockPlacement(){
        XxlJobHelper.log("秒杀库存落库");
        // 获取需要库存落库的秒杀活动
        List<SeckillSkuVO> seckillSkuList = seckillSkuService.listMaybeNeedStockPlacementActivity();

        if (CollectionUtil.isEmpty(seckillSkuList)) {
            return;
        }
        for (SeckillSkuVO seckillSkuVO : seckillSkuList) {
            RLock rLock = redissonClient.getLock(LockCacheNames.LOCK_SECKILL_ZONE_PERFIX + seckillSkuVO.getSeckillSkuId());
            try {
                rLock.lock();
                seckillSkuService.flushStockToDb(seckillSkuVO.getSeckillSkuId(),seckillSkuVO.getSeckillId());
            } finally {
                rLock.unlock();
            }
        }
    }


    /**
     * 已经结束的秒杀活动落库
     * 每个小时的中间时刻执行一次，获取上一个结束的活动
     * 比如 9点结束的活动，9点半，或者9点xx分的时候查下数据库，有没有还在处理但没有落库的订单
     * 这个9点xx分，通过定时任务查询
     */
    @XxlJob("coldStockPlacement")
    public void coldStockPlacement(){
        XxlJobHelper.log("秒杀库存落库");
        // 获取需要库存落库的秒杀活动
        List<SeckillSkuVO> seckillSkuList = seckillSkuService.listOverJustNow();

        if (CollectionUtil.isEmpty(seckillSkuList)) {
            return;
        }
        List<String> keys = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (SeckillSkuVO seckillSkuVO : seckillSkuList) {
            RLock rLock = redissonClient.getLock(LockCacheNames.LOCK_SECKILL_ZONE_PERFIX + seckillSkuVO.getSeckillSkuId());
            rLock.lock();
            try {
                seckillSkuService.flushStockToDb(seckillSkuVO.getSeckillSkuId(), seckillSkuVO.getSeckillId());
                keys.add(SeckillCacheNames.SECKILL_SKU_AVAILABLE_ZONE + seckillSkuVO.getSeckillSkuId());
                for (int i = 0; i < SeckillConstant.ZONE_NUM; i++) {
                    keys.add(SeckillCacheNames.SECKILL_SKU_STOCK + seckillSkuVO.getSeckillSkuId() + CacheNames.UNION_KEY + i);
                }
            } finally {
                rLock.unlock();
            }
        }
        // 已经落库后，把分片库存相关的缓存删除
        RedisUtil.del(keys);
    }
}
